/*
 * Copyright 2012 Finbudget.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package finbudgettable.client.ui.grid.cell.list;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.i18n.client.Messages;
import com.google.gwt.user.client.ui.*;

import finbudget.client.lists.GetListOfItems;
import finbudget.client.lists.ListArrivedEvent;
import finbudget.client.lists.ListArrivedHandler;
import finbudget.client.lists.ListsManager;
import finbudgettable.client.ui.grid.Utils;
import finbudgettable.client.ui.grid.column.FilterPanel;
import finbudgettable.client.ui.grid.filter.Filter;
import finbudgettable.client.ui.grid.filter.FilterChangeEvent;
import finbudgettable.client.ui.grid.filter.FilterChangeHandler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ListFilterPanel extends VerticalPanel implements FilterPanel {

    interface Strings extends Messages {
        @DefaultMessage("Фильтр:")
        String filter();

        @DefaultMessage("Значение")
        String value();

        @DefaultMessage("Установить")
        String applyFilter();

        @DefaultMessage("Равен {0}")
        String eqFilter(String value);

        @DefaultMessage("Неравен {0}")
        String neqFilter(String value);

    }

    interface FilterFactory {
        String getOp();

        Filter create(ListFilterPanel filterPanel);
    }

    interface FilterPresenter {
        void show(ListFilterPanel filterPanel, ListFilter filter);
    }

    private static final FilterFactory equalFilterFactory =
            new FilterFactory() {
                @Override
                public String getOp() {
                    return "=";
                }

                @Override
                public Filter create(ListFilterPanel filterPanel) {
                    return new EqualListFilter(filterPanel.fieldName, filterPanel.getStringValue(), filterPanel.getStringCaption());
                }
            };

    private static final FilterPresenter equalFilterPresenter =
            new FilterPresenter() {
                @Override
                public void show(ListFilterPanel filterPanel, ListFilter filter) {
                    String op = equalFilterFactory.getOp();
                    if (filterPanel.setOperationByName(op)) {
                        filterPanel.setValueByKey(filter.getValue());
                    }
                }
            };

    private static final FilterFactory notEqualFilterFactory =
            new FilterFactory() {
                @Override
                public String getOp() {
                    return "<>";
                }

                @Override
                public Filter create(ListFilterPanel filterPanel) {
                    return new NotEqualListFilter(filterPanel.fieldName, filterPanel.getStringValue(), filterPanel.getStringCaption());
                }
            };

    private static final FilterPresenter notEqualFilterPresenter =
            new FilterPresenter() {
                @Override
                public void show(ListFilterPanel filterPanel, ListFilter filter) {
                    String op = notEqualFilterFactory.getOp();
                    if (filterPanel.setOperationByName(op)) {
                        filterPanel.setValueByKey(filter.getValue());
                    }
                }
            };

    static final Strings STRINGS = GWT.create(Strings.class);
    private static final Map<String, FilterFactory> OPERATIONS = new HashMap<String, FilterFactory>();
    private static final Map<Class<?>, FilterPresenter> PRESENTERS = new HashMap<Class<?>, FilterPresenter>();
    private final ListBox opListBox;
    private final ListBox valueListBox;
    private final String fieldName;

    static {
        OPERATIONS.put(notEqualFilterFactory.getOp(), notEqualFilterFactory);
        OPERATIONS.put(equalFilterFactory.getOp(), equalFilterFactory);
    }

    static {
        PRESENTERS.put(NotEqualListFilter.class, notEqualFilterPresenter);
        PRESENTERS.put(EqualListFilter.class, equalFilterPresenter);
    }

    public ListFilterPanel(String fieldName, final ListsManager listsManager, final GetListOfItems.ListType listType) {
        this.fieldName = fieldName;

        List<GetListOfItems.BudgetListItem> list = listsManager.getList(listType);
        if (list.isEmpty()) {
            listsManager.addListArrivedEventHandler(new ListArrivedHandler() {
                @Override
                public void onListArrived(ListArrivedEvent event) {
                    if (event.getListType().equals(listType)) {
                        listsManager.removeListArrivedEventHandler(this);
                        fillListBox(event.getList());
                    }
                }
            });
        }

        Label filterLabel = new Label(STRINGS.filter());

        Label valueLabel = new Label(STRINGS.value());

        opListBox = new ListBox();
        for (String op : OPERATIONS.keySet()) {
            opListBox.addItem(op);
        }

        valueListBox = new ListBox(false);
        fillListBox(list);

        final Button applyButton = new Button(STRINGS.applyFilter());
        applyButton.setStyleName(Utils.blueButton);
        applyButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                String value = getStringValue();
                if (value != null) {
                    String op = opListBox.getValue(opListBox.getSelectedIndex());
                    fireEvent(new FilterChangeEvent(createFilter(op)));
                }
            }
        });

        valueListBox.addKeyDownHandler(new KeyDownHandler() {
            @Override
            public void onKeyDown(KeyDownEvent event) {
                if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                    applyButton.click();
                }
            }
        });


        Grid grid = new Grid(3, 2);
        grid.setStyleName(ListCell.RESOURCES.style().bListFilterPanel());
        grid.setWidget(0, 0, filterLabel);
        grid.setWidget(0, 1, opListBox);
        grid.setWidget(1, 0, valueLabel);
        grid.setWidget(1, 1, valueListBox);
        grid.setWidget(2, 1, applyButton);

        add(grid);
    }

    private void fillListBox(List<GetListOfItems.BudgetListItem> list) {
        valueListBox.clear();
        valueListBox.setEnabled(!list.isEmpty());
        for (GetListOfItems.BudgetListItem item : list) {
            valueListBox.addItem(item.name, item.key);
        }
    }

    private String getStringValue() {
        return valueListBox.getValue(valueListBox.getSelectedIndex());
    }


    protected Filter createFilter(String op) {
        FilterFactory factory = OPERATIONS.get(op);
        return factory.create(this);
    }

    @Override
    public void setFilter(Filter filter) {
        if (filter != null && filter instanceof ListFilter) {
            FilterPresenter presenter = PRESENTERS.get(filter.getClass());
            if (presenter != null) {
                presenter.show(this, (ListFilter) filter);
                return;
            }
        }
        valueListBox.setSelectedIndex(0);
        opListBox.setSelectedIndex(0);
    }

    @Override
    public void addFilterChangeHandler(FilterChangeHandler handler) {
        addHandler(handler, FilterChangeEvent.TYPE);
    }

    private boolean setOperationByName(String op) {
        for (int i = 0; i < opListBox.getItemCount(); i++) {
            if (op.equals(opListBox.getValue(i))) {
                opListBox.setSelectedIndex(i);
                return true;
            }
        }
        return false;
    }

    private boolean setValueByKey(String key) {
        for (int i = 0; i < valueListBox.getItemCount(); i++) {
            if (key.equals(valueListBox.getValue(i))) {
                valueListBox.setSelectedIndex(i);
                return true;
            }
        }
        return false;
    }

    private String getStringCaption() {
        return valueListBox.getItemText(valueListBox.getSelectedIndex());
    }

}
